package pl.krakow.knp.wikiboard.server;

import java.io.EOFException;
import java.net.Socket;
import java.io.ObjectInputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.SocketException;
import java.util.LinkedList;
import nu.xom.Builder;
import nu.xom.Document;
import nu.xom.ParsingException;
import pl.krakow.knp.wikiboard.server.xml.ActionXML;
import pl.krakow.knp.wikiboard.server.xml.UserXML;

/**
 * Class ServerThread
 */
public class ServerThread extends Thread {
    
    static LinkedList<ServerThread> clientsList = new LinkedList<ServerThread>();
    
    //stored as xml : <action type="login"><user id="0" name="user" leader="true" /></action>
    static LinkedList<String> userNamesListXML = new LinkedList<String>();
    
    private Socket socket;
    private ObjectOutputStream out;
    private ObjectInputStream in;
    
    private int id;
    private String name;
    
    private boolean leader;
    
    /**
     * Create a new instance of ServerThread
     * @param socket
     * @param admin
     * @param id
     */
    public ServerThread( Socket socket ) {
        this.socket = socket;
        try {
            this.out = new ObjectOutputStream(socket.getOutputStream());
            this.in = new ObjectInputStream(socket.getInputStream());     
            this.start();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
    
    @Override
    public void run() {
        
        // rozlaczenie z powodu istniejacej nazwy uzytkownika
        boolean userNameExistsDisconnection = false;
       
        synchronized(clientsList) {
            
            clientsList.add(this);
            id = clientsList.indexOf(this);
            if(this.id == 0)
                this.leader = true;
            else
                this.leader = false;
            System.out.println("Client " + id + " is connected from host " +
                    socket.getRemoteSocketAddress().toString().substring(1) );
        }
        try {
            // odbieram nazwe uzytkownika
            name = (String)in.readObject();
            if (checkDuplicate(name)) {
                System.out.println(name + "already exists!");
                String response = "username occupied";
                out.writeObject(response);
                userNameExistsDisconnection=true;
                throw new UserNameExistsException();
                
                //zeby nie uzywac this.stop(); zrobilem nowy typ wyjatku
            }
            else {
                System.out.println("ok!");
                String response = "username ok";
                out.writeObject(response);
            }
            
            this.sendStartupInfo();
            while (true) {
                sendToOthers( this.in.readObject() );
            }
        } catch (UserNameExistsException ex){
        } catch(EOFException ex) {
        } catch(Throwable t) {
            t.printStackTrace();
        } finally {
            try {
                in.close();
                out.close();
                socket.close();
            } catch(SocketException ex) {
                
            } catch(IOException ex) {
                ex.printStackTrace();
            } finally {
                synchronized(clientsList) {
                    UserXML u = new UserXML(this.id, name, leader);
                    ActionXML logout = new ActionXML(u, "logout");
                    if (userNameExistsDisconnection) 
                        //ustawiamy na poprzednia wartosc
                        userNameExistsDisconnection=false;
                    else
                        // o wylogowaniu wysylamy jesli nie nastapilo rozlaczenie z powodu istniejacej nazwy uzytkownika 
                        sendToOthers( logout.getXML().toXML() );
                    clientsList.remove(this);
                    ActionXML login = new ActionXML(u, "login");
                    userNamesListXML.remove( login.getXML().toXML() );
                    System.out.println("Disconnected client " + id);
                }
            }
        }
    }
    
    private void sendToOthers(Object element) {
        for(ServerThread client : clientsList) {
            synchronized(clientsList) {
                if (client != this)
                    client.writeObject(element);
            }
        }
    }
    
    private void sendToMyself(Object object) {
        this.writeObject(object);
    }
    
    private void writeObject(Object object) {
        try {
            out.writeObject(object);
            out.flush();
            out.reset();
        } catch(IOException ex) {
            System.out.println("IO Error with client " + id);
        }
    }
    
    private void sendStartupInfo() {        
        UserXML u = new UserXML(this.id, name, leader);
        ActionXML login = new ActionXML(u, "login");
        String loginXMLMessage = login.getXML().toXML();
        sendToOthers( loginXMLMessage );
        userNamesListXML.add(loginXMLMessage);
        // send to recently logged user whole list with names of already logged users
        for (String userName : userNamesListXML)
            sendToMyself(userName);
    }
    
    private boolean checkDuplicate (String name) throws ParsingException, IOException {
        for (String stringXML : userNamesListXML ) {
            Document doc = new Builder().build(stringXML, null);
            String existingName = doc.getRootElement().getFirstChildElement("user").getAttribute("name").getValue();
            if (existingName.equals(name))
                return true;
        }
        return false;
    }
}
